use nom::{
    bytes::complete::{is_not, tag, take_until},
    character::complete::{char, multispace0},
    multi::separated_list1,
    sequence::{delimited, tuple},
    IResult,
};

use crate::core::group::GroupData;
use crate::core::item::Item;

pub(crate) const ITEM_TAG: char = 'i';
pub(crate) const GROUP_TAG: char = 'g';

pub(crate) struct Entity {}

impl Entity {
    pub(crate) fn parse_group(input: &str) -> IResult<&str, GroupData> {
        let (_, name) = Self::parse_data(input)?;
        Ok(("", GroupData::new(name)))
    }

    pub(crate) fn parse_item(input: &str) -> IResult<&str, Item> {
        let (_, item_str) = Self::parse_data(input)?;
        let (_, out) = separated_list1(
            tag(","),
            tuple((
                multispace0,
                tag("\""),
                is_not("\""),
                take_until("\""),
                tag("\""),
                multispace0,
            )),
        )(item_str)?;

        let mut item = Item::default();
        for (_, _, value, _, _, _) in out {
            item.push(value);
        }

        Ok(("", item))
    }

    fn parse_data(input: &str) -> IResult<&str, &str> {
        delimited(char('['), is_not("]"), char(']'))(input)
    }

    pub fn stringfy_tem(item: &Item) -> String {
        let f = || String::from("");
        format!(
            "{}[\"{}\", \"{}\", \"{}\", \"{}\", \"{}\"]",
            ITEM_TAG,
            item.title.clone().unwrap_or_else(f),
            item.username.clone().unwrap_or_else(f),
            item.password.clone().unwrap_or_else(f),
            item.url.clone().unwrap_or_else(f),
            item.notes.clone().unwrap_or_else(f),
        )
    }

    pub fn stringfy_group(group: &GroupData) -> String {
        format!("[{}]", group.name())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn parse_group_works() {
        let name = "[jdkf]";
        let (_, group) = Entity::parse_group(name).unwrap();
        assert_eq!(String::from("jdkf"), *group.name());

        let name = "[你好]";
        let (_, group) = Entity::parse_group(name).unwrap();
        assert_eq!(String::from("你好"), *group.name());
    }

    #[test]
    fn parse_item_works() {
        let v = r#"["title1",   "username1"  ,  "password1", "http://www.zhissp.com", "备注备注"]"#;
        let (_, item) = Entity::parse_item(v).unwrap();
        assert_eq!(
            Item::new(
                Some(String::from("title1")),
                Some(String::from("username1")),
                Some(String::from("password1")),
                Some(String::from("http://www.zhissp.com")),
                Some(String::from("备注备注")),
            ),
            item
        );

        let v = r#"["title1",   "username1"  ,  "password1"]"#;
        let (_, item) = Entity::parse_item(v).unwrap();
        assert_eq!(
            Item::new(
                Some(String::from("title1")),
                Some(String::from("username1")),
                Some(String::from("password1")),
                None,
                None,
            ),
            item
        );

        let v = r#"["title1",   "username1"  ,  "password1", "http://www.zhissp.com", "备注备注", "多余的部分",  "生成item的时候不起作用"]"#;
        let (_, item) = Entity::parse_item(v).unwrap();
        assert_eq!(
            Item::new(
                Some(String::from("title1")),
                Some(String::from("username1")),
                Some(String::from("password1")),
                Some(String::from("http://www.zhissp.com")),
                Some(String::from("备注备注")),
            ),
            item
        );
    }
}
